﻿#include "pagecache.h"

// 获取一个K页的span
Span* PageCache::newSpan(size_t pageNum)
{
	assert(pageNum > 0);

	// 大于128 page的直接向堆申请
	if (pageNum > NPAGES-1)
	{
		void* ptr = SystemAlloc(pageNum);
		Span* span = m_spanPool.New();

		span->m_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
		span->m_n = pageNum;

		m_idSpanMap.set(span->m_pageId, span);

		return span;
	}

	// 先检查第k个桶里面有没有span
	if (!m_spanLists[pageNum].empty())
	{
		Span* kSpan = m_spanLists[pageNum].popFront();

		// 建立id和span的映射，方便central cache回收小块内存时，查找对应的span
		for (PAGE_ID i = 0; i < kSpan->m_n; ++i)
		{
			m_idSpanMap.set(kSpan->m_pageId + i, kSpan);
		}

		return kSpan;
	}

	// 检查一下后面的桶里面有没有span，如果有可以把他它进行切分
	for (size_t i = pageNum+1; i < NPAGES; ++i)
	{
		if (!m_spanLists[i].empty())
		{
			Span* nSpan = m_spanLists[i].popFront();
			Span* kSpan = m_spanPool.New();

			// 在nSpan的头部切一个k页下来
			// k页span返回
			// nSpan再挂到对应映射的位置
			kSpan->m_pageId = nSpan->m_pageId;
			kSpan->m_n = pageNum;

			nSpan->m_pageId += pageNum;
			nSpan->m_n -= pageNum;

			m_spanLists[nSpan->m_n].pushFront(nSpan);
			// 存储nSpan的首位页号跟nSpan映射，方便page cache回收内存时
			// 进行的合并查找
			m_idSpanMap.set(nSpan->m_pageId, nSpan);
			m_idSpanMap.set(nSpan->m_pageId + nSpan->m_n - 1, nSpan);

			// 建立id和span的映射，方便central cache回收小块内存时，查找对应的span
			for (PAGE_ID i = 0; i < kSpan->m_n; ++i)
			{
				m_idSpanMap.set(kSpan->m_pageId + i, kSpan);
			}

			return kSpan;
		}
	}

	// 走到这个位置就说明后面没有大页的span了
	// 这时就去找堆要一个128页的span
	//Span* bigSpan = new Span;
	Span* bigSpan = m_spanPool.New();
	void* ptr = SystemAlloc(NPAGES - 1);
	bigSpan->m_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
	bigSpan->m_n = NPAGES - 1;

	m_spanLists[bigSpan->m_n].pushFront(bigSpan);

	return newSpan(pageNum);
}

Span* PageCache::mapObjectToSpan(void* obj)
{
	PAGE_ID id = ((PAGE_ID)obj >> PAGE_SHIFT);

	auto ret = (Span*)m_idSpanMap.get(id);
	assert(ret != nullptr);
	return ret;
}

void PageCache::releaseSpanToPageCache(Span* span)
{
	// 大于128 page的直接还给堆
	if (span->m_n > NPAGES-1)
	{
		void* ptr = (void*)(span->m_pageId << PAGE_SHIFT);
		SystemFree(ptr);
		//delete span;
		m_spanPool.Delete(span);

		return;
	}

	// 对span前后的页，尝试进行合并，缓解内存碎片问题
	// 向前合并
	while (true)
	{
		PAGE_ID prevId = span->m_pageId - 1;
		auto ret = (Span*)m_idSpanMap.get(prevId);

		// 没有则不合并
		if (ret == nullptr)
		{
			break;
		}

		// 前面相邻页的span在使用，不合并了
		Span* prevSpan = ret;
		if (prevSpan->m_isUse == true)
		{
			break;
		}

		// 合并出超过128页的span没办法管理，不合并了
		if (prevSpan->m_n + span->m_n > NPAGES-1)
		{
			break;
		}

		// 合并span
		span->m_pageId = prevSpan->m_pageId; // 更新起始页号
		span->m_n += prevSpan->m_n; // 更新页数

		// 从自由链表中移除被合并的span
		m_spanLists[prevSpan->m_n].erase(prevSpan);

		//delete prevSpan;
		m_spanPool.Delete(prevSpan);
	}

	// 向后合并
	while (true)
	{
		PAGE_ID nextId = span->m_pageId + span->m_n;
		auto ret = (Span*)m_idSpanMap.get(nextId);
		
		// 没有则不合并
		if (ret == nullptr)
		{
			break;
		}

		Span* nextSpan = ret;
		// 后面的页在使用，不合并了
		if (nextSpan->m_isUse == true)
		{
			break;
		}

		// 合并出超过128页的span没办法管理，不合并了
		if (nextSpan->m_n + span->m_n > NPAGES-1)
		{
			break;
		}

		// 合并span
		// 不要更新起始页号，因为是向后合并
		span->m_n += nextSpan->m_n; // 更新页数

		// 从自由链表中移除被合并的span
		m_spanLists[nextSpan->m_n].erase(nextSpan);

		//delete nextSpan;
		m_spanPool.Delete(nextSpan);
	}

	m_spanLists[span->m_n].pushFront(span);
	span->m_isUse = false;
	// 建立id和span的映射，方便central cache回收小块内存时，查找对应的span
	m_idSpanMap.set(span->m_pageId, span);
	m_idSpanMap.set(span->m_pageId + span->m_n - 1, span);
}

void PageCache::lock()
{
	m_pageMutex.lock();
}

void PageCache::unlock()
{
	m_pageMutex.unlock();
}